// File MessageDispatcher.cpp
//
// Purpose: A message dispatcher. Manages messages of the type 
//          Telegram. Instantiated as a singleton.
//
// Author : Bryson King (edited)
// Original Code Provided by: Mat Buckland 2002 (fup@ai-junkie.com)
// Date : 18 March 2013
// --------------------------------------------------------------
#define DEBUG_SECTION
//#pragma warning (disable:4786)

#include "MessageDispatcher.h"

#include <iostream>

#include "misc\ConsoleUtils.h"
#include "BaseGameEntity.h"
#include "CrudeTimer.h"
#include "EntityManager.h"
#include "MessageTypes.h"
#include "EntityNames.h"

using std::cout;

using std::set;

CMessageDispatcher::CMessageDispatcher(){}

//------------------------------ Instance -------------------------------------
CMessageDispatcher* CMessageDispatcher::Instance()
{
	static CMessageDispatcher instance;
	return &instance;
}

//----------------------------- Dispatch ---------------------------------
//  
//  see description in header
//------------------------------------------------------------------------
void CMessageDispatcher::Discharge(CBaseGameEntity* pReceiver, const Telegram& telegram)
{
	if (!pReceiver->HandleMessage(telegram))
	{
		//telegram could not be handled
		cout << "Message not handled";
	}
}

//---------------------------- DispatchMessage ---------------------------
//
//  given a message, a receiver, a sender and any time delay , this function
//  routes the message to the correct agent (if no delay) or stores
//  in the message queue to be dispatched at the correct time
//------------------------------------------------------------------------
void CMessageDispatcher::DispatchMessageBase(double  delay, int sender, int receiver, 
										 int msg, void* ExtraInfo)
{
	SetTextColor(BACKGROUND_RED|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);

	//get pointers to the sender and receiver
	CBaseGameEntity* pSender   = EntityMgr->GetEntityFromID(sender);
	CBaseGameEntity* pReceiver = EntityMgr->GetEntityFromID(receiver);

	//make sure the receiver is valid
	if (pReceiver == NULL)
	{
		cout << "\nWarning! No Receiver with ID of " << receiver << " found";
		return;
	}
  
	//create the telegram
	Telegram telegram(0, sender, receiver, msg, ExtraInfo);
  
	//if there is no delay, route telegram immediately                       
	if (delay <= 0.0f)                                                        
	{
		cout << "\nInstant telegram dispatched at time: " << Clock->GetCurrentTime() << 
			" by " << GetNameOfEntity(pSender->ID()) << " for " << GetNameOfEntity(pReceiver->ID()) << 
			". Msg is "<< MsgToStr(msg);

		//send the telegram to the recipient
		Discharge(pReceiver, telegram);
	}
	//else calculate the time when the telegram should be dispatched
	else
	{
		double CurrentTime = Clock->GetCurrentTime(); 

		telegram.DispatchTime = CurrentTime + delay;

		//and put it in the queue
		PriorityQ.insert(telegram);   

		cout << "\nDelayed telegram from " << GetNameOfEntity(pSender->ID()) << " recorded at time " 
			<< Clock->GetCurrentTime() << " for " << GetNameOfEntity(pReceiver->ID())
			<< ". Msg is "<< MsgToStr(msg);
            
	}
}


//---------------------- DispatchDelayedMessages -------------------------
//
//  This function dispatches any telegrams with a timestamp that has
//  expired. Any dispatched telegrams are removed from the queue
//------------------------------------------------------------------------
void CMessageDispatcher::DispatchDelayedMessages()
{
	SetTextColor(BACKGROUND_RED|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
  
	//get current time
	double CurrentTime = Clock->GetCurrentTime();

	//now peek at the queue to see if any telegrams need dispatching.
	//remove all telegrams from the front of the queue that have gone
	//past their sell by date
	while( !PriorityQ.empty() && (PriorityQ.begin()->DispatchTime < CurrentTime) && 
			(PriorityQ.begin()->DispatchTime > 0) )
	{
		//read the telegram from the front of the queue
		const Telegram& telegram = *PriorityQ.begin();

		//find the recipient
		CBaseGameEntity* pReceiver = EntityMgr->GetEntityFromID(telegram.Receiver);

		cout << "\nQueued telegram ready for dispatch: Sent to "
			<< GetNameOfEntity(pReceiver->ID()) << ". Msg is " << MsgToStr(telegram.Msg);

		//send the telegram to the recipient
		Discharge(pReceiver, telegram);

		//remove it from the queue
		PriorityQ.erase(PriorityQ.begin());
	}
}